package com.iot.service.mqtt.model.topic;

import com.iot.service.mqtt.model.entity.TlSubClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Stack;

/**
 * @Author: hszhou
 * @Date: 2025/4/30 14:56
 * @Description: 字典树 存储订阅关系
 */
@Component
public class TlTopicTrie {

    private static final Logger logger = LoggerFactory.getLogger(TlTopicTrie.class);

    public final static String  SLASH ="/";

    private final static String HASH  = "#";

    private final static String  PLUS ="+";

    /** 根节点 不保持任何数据*/
    private final TlTopicNode root;

    public TlTopicTrie() {
        root = new TlTopicNode();
    }


    /**
     * @description: 插入一条数据
     * @author: hszhou
     * @datetime: 2025-05-06 09:18:56
     * @param:
     * @param: topic 主题名称
     * @param: client 主题的客户端
     * @return: void
     **/
    public void insert(String topic, TlSubClient client){

        String[] segments = topic.split(SLASH);
        TlTopicNode current = root;
        for (int i = 0; i < segments.length; i++) {
            //遍历主题的每个部分
            String segment = segments[i];
            boolean isLast = (i == segments.length - 1);
            // 处理通配符规则
            if (segment.equals(HASH)) {
                //如果是#通配符 那么必须在最后一个位置
                if (!isLast) {
                    throw new IllegalArgumentException("# must be the last segment");
                }
                handleMultiWildcard(current, client);
                return;
            } else if (segment.equals(PLUS)) {
                // 如果是+通配符
                current = handleSingleWildcard(current);
            } else {
                //如果是普通节点
                current = handleNormalSegment(current, segment);
            }
        }

        //等待遍历完成后 这个current就是最后一个节点 将client保存到最后的一个节点
        current.getClients().put(client.getClientId(), client);
    }

    /**
     * @description: 给节点添加通配符节点 节点的通配符节点只可能存在一个#通配符的节点
     * @author: hszhou
     * @datetime: 2025-05-06 15:44:34
     * @param:
     * @param: parent 父节点 就是#的上一个节点
     * @param: client 需要保存的客户端
     * @return: void 不需要返回
     **/
    private void handleMultiWildcard(TlTopicNode parent, TlSubClient client) {

        /*如果当前节点没有#通配符的子节点
         * 那么就构造一个 然后设置进入父节点 并且这个节点的父节点设置一下，后续清除的时候需要
         * 如果已经有了#通配符的子节点 只可能有一个 就将client加入到这个子节点的客户端列表中
         * */
        if (parent.getMultiWildcardNode() == null) {
            TlTopicNode node = new TlTopicNode();
            node.setType(NodeType.MULTI_LEVEL_WILDCARD);
            // 设置父节点
            node.setParent(parent);
            parent.setMultiWildcardNode(node);
        }
        parent.getMultiWildcardNode().getClients().put(client.getClientId(), client);
    }

    /**
     * @description: 处理节点是+通配符
     * @author: hszhou
     * @datetime: 2025-05-06 15:48:47
     * @param: parent 父节点 也就是上一个节点
     * @return: TlTopicNode 处理后的节点
     **/
    private TlTopicNode handleSingleWildcard(TlTopicNode parent) {

        /*父节点的+通配符节点也只能存在一个
         * 比如说a/+ 那么a扣手parent 那么a下面只能存在一个+
         * 如果不存在+的子节点 那么就添加一个  并且设置新的节点的父亲节点就是该节点
         * 如果存在+的子节点 那么就直接返回这个子节点  因为并不切断这个节点就是最后一个节点 所有不需要将client保存进入
         * */
        if (parent.getSingleWildcardNode() == null) {
            TlTopicNode node = new TlTopicNode();
            node.setType(NodeType.SINGLE_LEVEL_WILDCARD);
            node.setParent(parent);
            parent.setSingleWildcardNode(node);
        }
        return parent.getSingleWildcardNode();
    }

    /**
     * @description: 处理普通节点
     * @author: hszhou
     * @datetime: 2025-05-06 15:53:32
     * @param:
     * @param: parent 父节点
     * @param: segment 分割后的字符串
     * @return: TlTopicNode 分割后的字符串所创建的节点
     **/
    private TlTopicNode handleNormalSegment(TlTopicNode parent, String segment) {

        /*
         * 构建这个节点，然后保存到父节点的普通节点的列表中
         * 正常情况下 使用这些节点都是用的是list，但是为了查找方便 所有使用的是map，
         * @author: hszhou
         **/
        return parent.getChildrenMap().computeIfAbsent(segment, k -> {
            TlTopicNode node = new TlTopicNode();
            node.setParent(parent); // 设置父节点
            return node;
        });
    }
    public void remove(String topic, TlSubClient client){

        if (StringUtils.isEmpty(topic)) {
            return;
        }

        String[] segments = topic.split("/");
        /*创建一个栈 为了记录遍历到路线 也就是parent的路线 后续通过这个栈里面的数据进行回溯删除*/
        Stack<TlTopicNode> pathStack = new Stack<>();
        TlTopicNode current = root;

        // Step 1: 遍历路径，记录节点路径栈
        for (String segment : segments) {
            // 记录父节点路径
            pathStack.push(current);

            // 处理通配符规则
            if (segment.equals(HASH)) {
                current = current.getMultiWildcardNode();
                break; // # 必须是最后一个段
            } else if (segment.equals(PLUS)) {
                current = current.getSingleWildcardNode();
            } else {
                current = current.getChildrenMap().get(segment);
            }
            // 路径不存在
            if (current == null){
                return ;
            }
        }

        // Step 2: 删除客户端（仅叶子节点有效） 这个current就是叶子节点
        if (current != null && !current.getClients().isEmpty()) {
            // 需确保 TlSubClient 正确实现 equals()
            current.getClients().remove(client.getClientId());
            // Step 3: 反向清理空节点
            if (current.getClients().isEmpty()) {
                cleanupEmptyNodes(current, pathStack);
            }
        }

    }
    /**
     * 从叶子节点向上清理空节点
     * @param node       当前待清理节点
     * @param pathStack  路径栈（从根到当前节点的父节点）
     */
    private void cleanupEmptyNodes(TlTopicNode node, Stack<TlTopicNode> pathStack) {
        TlTopicNode current = node;

        while (!pathStack.isEmpty()) {
            TlTopicNode parent = pathStack.pop();

            // 判断当前节点是否可删除（无客户端、无子节点、无通配符子节点）
            boolean canRemove = current.getClients().isEmpty()
                    && current.getChildrenMap().isEmpty()
                    && current.getSingleWildcardNode() == null
                    && current.getMultiWildcardNode() == null;

            if (!canRemove) {
                break;
            }

            // 从父节点中移除当前节点
            if (parent.getChildrenMap().containsValue(current)) {
                // 普通子节点
                TlTopicNode finalCurrent = current;
                parent.getChildrenMap().entrySet().removeIf(entry -> entry.getValue() == finalCurrent);
            } else if (parent.getSingleWildcardNode() == current) {
                // 单层通配符+子节点
                parent.setSingleWildcardNode(null);
            } else if (parent.getMultiWildcardNode() == current) {
                // 多层通配符#子节点
                parent.setMultiWildcardNode(null);
            }
            // 继续向上检查父节点
            current = parent;
        }
    }
    public List<TlSubClient> search(String topic) {
        List<TlSubClient> result = new ArrayList<>();
        String[] segments = topic.split("/");
        searchRecursive(root, segments, 0, result);
        return result;
    }

    private void searchRecursive(TlTopicNode node, String[] segments, int depth, List<TlSubClient> result) {
        // 终止条件：已遍历所有段
        if (depth == segments.length) {
            collectAllClients(node, result);
            return;
        }

        String currentSegment = segments[depth];

        // 匹配普通节点
        TlTopicNode child = node.getChildrenMap().get(currentSegment);
        if (child != null) {
            searchRecursive(child, segments, depth + 1, result);
        }

        // 匹配单层通配符+
        if (node.getSingleWildcardNode() != null) {
            searchRecursive(node.getSingleWildcardNode(), segments, depth + 1, result);
        }

        // 匹配多层通配符#
        if (node.getMultiWildcardNode() != null) {
            collectAllClients(node.getMultiWildcardNode(), result);
        }
    }

    private void collectAllClients(TlTopicNode node, List<TlSubClient> result) {
        Collection<TlSubClient> clients = node.getClients().values();
        result.addAll(clients);
        // 递归收集所有子节点的客户端（针对多层通配符）
        for (TlTopicNode child : node.getChildrenMap().values()) {
            collectAllClients(child, result);
        }
    }
}